use std::{
    sync::{
        atomic::{AtomicU64, AtomicU8, Ordering},
        Arc,
    },
    time::Duration,
};

use axum::{body::Bytes, Router};
use dashmap::DashMap;
use tokio::{
    sync::{mpsc, oneshot, Mutex},
    time::timeout,
};

use crate::{
    error::{AppErr, EC_NO_LOGIN, EC_RECV_TIMEOUT, EC_RX_WAIT_TIMEOUT, EC_TOKEN_INVALID},
    utils::{current_timestamp, token_create, UninitCell},
};

mod device;

pub const TY_NOTIFY: u8 = 0;
pub const TY_REQ: u8 = 1;
// pub const TY_RES: u8 = 2;

pub struct MsgItem {
    pub seq: u8,
    pub ty: u8,
    pub body: Bytes,
    pub cmd: String,
}

struct MessageQueue {
    seq: AtomicU8,
    tx: mpsc::Sender<MsgItem>,
    rx: Mutex<mpsc::Receiver<MsgItem>>,
    router: DashMap<u8, oneshot::Sender<Bytes>>,
}

struct Inner {
    _id: i64,
    token: String,
    mq: MessageQueue,
    poll_timestamp: AtomicU64,
}

type Shared = Arc<Inner>;

static DEVICE_MAP: UninitCell<DashMap<i64, Shared>> = UninitCell::uninit();

pub fn init_once() {
    unsafe {
        DEVICE_MAP.init_once(DashMap::new());
    }
}

pub fn login(id: i64, mac_addr: &str) -> String {
    let token = token_create(mac_addr);
    let token2 = token.clone();
    let (tx, rx) = mpsc::channel(32);
    let info = Inner {
        _id: id,
        token,
        poll_timestamp: AtomicU64::new(current_timestamp()),
        mq: MessageQueue {
            seq: AtomicU8::new(0),
            tx,
            rx: Mutex::new(rx),
            router: DashMap::new(),
        },
    };
    DEVICE_MAP.insert(id, Arc::new(info));
    token2
}

fn login_assert(id: i64, token: &str) -> Result<Arc<Inner>, AppErr> {
    let ret = DEVICE_MAP
        .get(&id)
        .ok_or(AppErr::new_service(EC_NO_LOGIN, "没有登陆"))?;
    if ret.token.as_str() != token {
        Err(AppErr::new_service(EC_TOKEN_INVALID, "token无效"))
    } else {
        Ok(ret.value().clone())
    }
}

pub async fn poll(id: i64, token: &str) -> Result<MsgItem, AppErr> {
    let inner = login_assert(id, token)?;
    inner
        .poll_timestamp
        .store(current_timestamp(), Ordering::Relaxed);

    let rx = inner.mq.rx.lock();

    let mut rx = timeout(Duration::from_secs(10), rx)
        .await
        .map_err(|_| AppErr::new_service(EC_RX_WAIT_TIMEOUT, "等待rx超时"))?;

    let item = timeout(Duration::from_secs(10), rx.recv())
        .await
        .map_err(|_| AppErr::new_service(EC_RECV_TIMEOUT, "接收超时"))?
        .ok_or(AppErr::from_static("接收出错"))?;
    Ok(item)
}

pub async fn post_notify(id: i64, cmd: &str, item: Bytes) -> Result<(), AppErr> {
    let inner = DEVICE_MAP
        .get(&id)
        .ok_or(AppErr::from_static("没有对应的设备"))?
        .value()
        .clone();
    let seq = 0;
    inner
        .mq
        .tx
        .send(MsgItem {
            seq,
            ty: TY_NOTIFY,
            body: item,
            cmd: cmd.to_owned(),
        })
        .await
        .map_err(|_| AppErr::from_static("未知错误"))?;

    Ok(())
}

pub fn post_res(id: i64, seq: u8, item: Bytes) -> Result<(), AppErr> {
    let inner = DEVICE_MAP
        .get(&id)
        .ok_or(AppErr::from_static("没有对应的设备"))?
        .value()
        .clone();

    let tx = inner
        .mq
        .router
        .remove(&seq)
        .ok_or(AppErr::from_static("没有对应seq的req"))?;
    tx.1.send(item)
        .map_err(|_| AppErr::from_static("未知错误"))?;
    Ok(())
}

pub async fn post_req(id: i64, cmd: &str, item: Bytes, time_sec: u32) -> Result<Bytes, AppErr> {
    let inner = DEVICE_MAP
        .get(&id)
        .ok_or(AppErr::from_static("没有对应的设备"))?
        .value()
        .clone();
    let seq = inner.mq.seq.fetch_add(1, Ordering::Relaxed);
    let msg_item = MsgItem {
        seq,
        ty: TY_REQ,
        body: item,
        cmd: cmd.to_owned(),
    };
    let (tx, rx) = oneshot::channel();
    inner.mq.router.insert(seq, tx);
    inner
        .mq
        .tx
        .send(msg_item)
        .await
        .map_err(|_| AppErr::from_static("未知错误2"))?;
    let res = timeout(Duration::from_secs(time_sec as u64), rx)
        .await
        .map_err(|_| AppErr::from_static("等待返回超时"))?
        .map_err(|_| AppErr::from_static("未知错误"))?;
    Ok(res)
}

pub fn poll_timestamp(id: i64) -> Result<u64, AppErr> {
    let inner = DEVICE_MAP
        .get(&id)
        .ok_or(AppErr::from_static("没有对应的设备"))?
        .value()
        .clone();
    Ok(inner.poll_timestamp.load(Ordering::Relaxed))
}

pub fn register() -> Router {
    Router::new().nest("/device", device::register())
}
